/**
 * @file pp_buffer.c
 * @author monster
 * @date 2024/4/15
 */

/*********************
 *      INCLUDES
 *********************/
#include "pp_buffer.h"
#include <string.h>

/*********************
 *      DEFINES
 *********************/

/**********************
 *      TYPEDEFS
 **********************/

/**********************
 *  STATIC VARIABLES
 **********************/

/**********************
 *  STATIC PROTOTYPES
 **********************/

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/

/**
 * @brief Ping-pong buffer initialization
 * @param ppbuf Pointer to the ping-pong buffer structure
 * @param buf0 Pointer to the first buffer
 * @patam buf1 Pointer to the second buffer
 */
void pp_buffer_init(pp_buffer_t *ppbuf, void *buf0, void *buf1, uint32_t size) {
    memset(ppbuf, 0, sizeof(pp_buffer_t));
    ppbuf->buffer[0] = buf0;
    ppbuf->buffer[1] = buf1;
    ppbuf->size = size;
}

/**
 * @brief Get a readable buffer
 * @param ppbuf Pointer to the ping-pong buffer structure
 * @param p_buf Pointer to the pointer to the buffer to be read
 * @retval Returns true if there is a buffer to be read
 */
bool pp_buffer_get_read_buffer(pp_buffer_t *ppbuf, void **p_buf) {
    uint8_t read_index = !ppbuf->write_index;

    if (ppbuf->read_available[read_index]) {
        ppbuf->read_index = read_index;
    } else if (ppbuf->read_available[!read_index]) {
        ppbuf->read_index = !read_index;
    } else {
        return false;
    }

    *p_buf = ppbuf->buffer[ppbuf->read_index];
    return true;
}

/**
 * @brief  Notify buffer read completion
 * @param  ppbuf: Pointer to the ping-pong buffer structure
 * @retval None
 */
void pp_buffer_set_read_done(pp_buffer_t *ppbuf) {
    ppbuf->read_available[ppbuf->read_index] = false;
}

/**
 * @brief Buffer read copy data
 * @param ppbuf Pointer to the ping-pong buffer structure
 * @param p_buf Pointer to read out
 * @param size  Read data size
 * @retval Returns true if there is a buffer to be read
 */
bool pp_buffer_read_data(pp_buffer_t *ppbuf, void *p_buf, uint32_t size) {
    void    *buffer;
    uint8_t ret = pp_buffer_get_read_buffer(ppbuf, &buffer);
    if (buffer == NULL) {
        return false;
    }

    if (ret) {
        size = size > ppbuf->size ? ppbuf->size : size;
        memcpy(p_buf, buffer, size);
        pp_buffer_set_read_done(ppbuf);
    }

    return ret;
}

/**
 * @brief  Get writable buffer
 * @param  ppbuf:	Pointer to the ping-pong buffer structure
 * @param  p_buf:	Pointer to the pointer to the buffer to be wriye
 * @retval None
 */
void pp_buffer_get_write_buffer(pp_buffer_t *ppbuf, void **p_buf) {
    ppbuf->read_available[ppbuf->write_index] = false;
    *p_buf = ppbuf->buffer[ppbuf->write_index];
}

/**
 * @brief  Notify buffer write completion
 * @param  ppbuf: Pointer to the ping-pong buffer structure
 * @retval None
 */
void pp_buffer_set_write_done(pp_buffer_t *ppbuf) {
    ppbuf->read_available[ppbuf->write_index] = true;
    ppbuf->write_index = !ppbuf->write_index;
}

/**
 * @brief Buffer write copy data
 * @param ppbuf Pointer to the ping-pong buffer structure
 * @param p_buf Pointer to read out
 * @param size  Read data size
 * @retval Returns true if there is a buffer to be read
 */
void pp_buffer_write_data(pp_buffer_t *ppbuf, void *p_buf, uint32_t size) {
    void *buffer;
    pp_buffer_get_write_buffer(ppbuf, &buffer);
    if (buffer == NULL) {
        return;
    }

    size = (size > ppbuf->size) ? ppbuf->size : size;
    memcpy(buffer, p_buf, size);

    pp_buffer_set_write_done(ppbuf);
}
